package csp;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import data.*;
import data.Binary.BinaryType;
import data.Unary.UnaryType;

public class CSP {
	
	private ArrayList<Item> variables;
	private ArrayList<Bag> values;
	public FittingLimit fittingLimit;
	
	public CSP(HashMap<String, Item> variables, HashMap<String, Bag> values, FittingLimit fittingLimit)
	{
		this.fittingLimit=fittingLimit;
		
		this.variables=new ArrayList<Item>();
		for (String key : variables.keySet()) {
			this.variables.add(variables.get(key));
		}
		
		this.values=new ArrayList<Bag>();
		for (String key : values.keySet()) {
			this.values.add(values.get(key));
		}
		
		nodeConsistency();
	}
	
	public CSP(CSP csp)
	{
		this.variables=csp.Variables();
		this.values=csp.Values();
		this.fittingLimit=csp.fittingLimit;
	}
	
	public ArrayList<Item> Variables() {
		return variables;
	}
	
	public ArrayList<Bag> Values() {
		return values;
	}
	
	private void nodeConsistency() {
		for (Item var : this.variables) {
			//remove entries in var's domain that conflict with var's unary constraints
			for (Unary constraint : var.UnaryConstraints) {
				if (!constraint.IsSatisfied()) {
					if (constraint.Type == UnaryType.Exclusive) {
						for (Bag bag : constraint.Values) {
							var.Domain.remove(bag);
						}
					} else {
						ArrayList<Bag> oldDom = (ArrayList<Bag>)var.Domain.clone();
						for (Bag bag : oldDom) {
							if (!constraint.Values.contains(bag)) {
								var.Domain.remove(bag);
							}
						}
					}
				}
			}
		}
	}
	
	public void forwardCheck( HashMap<Item, Bag> assignments, Item last) {
		Bag bag = assignments.get(last);
		//check constraints involving "last", using data from assignments. Cut obvious violations from this CSPs domains. 
		for (Binary binary : last.BinaryConstraints) {
			if (binary.Type == BinaryType.NotEqual) {
				Item other = binary.VariableA.Name.equals(last.Name) ?
							 binary.VariableB : binary.VariableA;
				other.Domain.remove(bag);
			}
		}
		
		for (MutualExclusive mutualExclusive : last.MutualExclusiveConstraints) {
			if (mutualExclusive.ValueA.Name.equals(bag.Name) ||
				mutualExclusive.ValueB.Name.equals(bag.Name)) {

				Item otherItem = mutualExclusive.VariableA.Name.equals(last.Name) ?
							 	 mutualExclusive.VariableB : mutualExclusive.VariableA;
				Bag otherBag = mutualExclusive.ValueA.Name.equals(bag.Name) ?
					 	 	   mutualExclusive.ValueA : mutualExclusive.ValueB;
				otherItem.Domain.remove(otherBag);
			}
		}
	}
}
